package com.atguigu.exam.service.impl;

import com.atguigu.exam.entity.*;
import com.atguigu.exam.exception.ExamException;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionQueryDto;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {


    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public Page<Question> getPageByCondition(Page<Question> questionPage, QuestionQueryDto queryDto) {
        //调用QuestionMapper的分页及带条件查询的方法
        Page<Question> questionPage1 = baseMapper.getPageByCondition(questionPage, queryDto);
        return questionPage1;
    }

    @Override
    public Page<Question> getPageByConditionWithStream(Page<Question> questionPage, QuestionQueryDto queryDto) {
        //创建LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //封装查询条件
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getCategoryId()),Question::getCategoryId,queryDto.getCategoryId());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getDifficulty()),Question::getDifficulty,queryDto.getDifficulty());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getType()),Question::getType,queryDto.getType());
        questionLambdaQueryWrapper.like(ObjectUtils.isNotEmpty(queryDto.getKeyword()),Question::getTitle,queryDto.getKeyword());
        //调用自带的分页及带条件查询的方法查询题目列表
        questionPage = baseMapper.selectPage(questionPage, questionLambdaQueryWrapper);
        //获取当前页中的集合
        List<Question> pageQuestionList = questionPage.getRecords();
        //验空
        if(!CollectionUtils.isEmpty(pageQuestionList)){
            //给题目设置答案和选项
            fillAnswerAndChoices(pageQuestionList);
        }
        return questionPage;
    }

    @Override
    public Page<Question> getPageByConditionWithEasy(Page<Question> questionPage, QuestionQueryDto queryDto) {
        //创建LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //封装查询条件
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getCategoryId()),Question::getCategoryId,queryDto.getCategoryId());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getDifficulty()),Question::getDifficulty,queryDto.getDifficulty());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getType()),Question::getType,queryDto.getType());
        //调用自带的分页及带条件查询的方法查询题目列表
        questionPage = baseMapper.selectPage(questionPage, questionLambdaQueryWrapper);
        //获取当前页中的集合
        List<Question> pageQuestionList = questionPage.getRecords();
        //遍历当前页中的题目列表
        pageQuestionList.forEach(question -> {
            //获取当前题目的ID
            Long questionId = question.getId();
            //根据题目ID查询题目答案
            QuestionAnswer questionAnswer = questionAnswerMapper.selectOne(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId,questionId));
            //设置答案
            question.setAnswer(questionAnswer);
            //判断当前题是不是选择题或判断题
            if("CHOICE".equals(question.getType()) || "JUDGE".equals(question.getType())){
                //根据题目id获取所有的选项
                //创建LambdaQueryWrapper对象，用于构建查询条件
                LambdaQueryWrapper<QuestionChoice> questionChoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
                //封装查询条件
                questionChoiceLambdaQueryWrapper.eq(QuestionChoice::getQuestionId,question.getId());
                //调用QuestionChoiceMapper的带条件查询的方法
                List<QuestionChoice> questionChoices = questionChoiceMapper.selectList(questionChoiceLambdaQueryWrapper);
                //排序
                questionChoices.sort((o1, o2) -> o1.getSort().compareTo(o2.getSort()));
                //将选项设置到题目中
                question.setChoices(questionChoices);
            }
        });
        return questionPage;
    }

    @Override
    public Question getQuestionDetailsById(Long id) {
        Question questionDetailsById = baseMapper.getQuestionDetailsById(id);
        //开启一个线程，异步加载统计题目访问的次数
        new Thread(() -> {
            //向Redis中添加当前题目访问的次数
            redisTemplate.opsForZSet().incrementScore("question:popular",id,1.0);
        }
        ).start();
        return questionDetailsById;
    }

    @Transactional
    @Override
    public void createQuestion(Question question) {
        //判断在当前类型种是否已经存在该题目
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<Question>().eq(Question::getType, question.getType()).eq(Question::getTitle, question.getTitle()));
        if(exists){
            //抛出异常
            throw new ExamException(444,"在<"+question.getType()+">类型中已经存在该题目");
        }
        //1.调用MyBatis Plus自带的方法保存题目
        baseMapper.insert(question);
        //获取题目的答案
        QuestionAnswer answer = question.getAnswer();
        //获取数据库自增的题目的ID
        Long questionId = question.getId();
        //设置题目ID
        answer.setQuestionId(questionId);
        //判断是否是选择题
        if("CHOICE".equals(question.getType())){
            //获取所有的选项
            List<QuestionChoice> choices = question.getChoices();
            //创建一个StringBuilder对象，用于拼接选项内容
            StringBuilder choiceContent = new StringBuilder();
            //遍历
            for (int i = 0; i < choices.size(); i++) {
                //获取每一个选项
                QuestionChoice choice = choices.get(i);
                //设置题目ID
                choice.setQuestionId(questionId);
                //设置选项的排序
                choice.setSort(i);
                //2.如果是选择题保存选项
                questionChoiceMapper.insert(choice);
                //判断当前选项是否是正确答案
                if(choice.getIsCorrect()){
                    if (choiceContent.length() > 0){
                        choiceContent.append(",");
                    }
                    choiceContent.append((char)('A'+i));
                }
            }

            //将正确答案设置到answer中
            answer.setAnswer(choiceContent.toString());
        }

        //3.保存答案
        questionAnswerMapper.insert(answer);
    }

    @Transactional
    @Override
    public void updateQuestion(Question question) {
        //判断更新之后的题目是否在当前类型中已经存在
        //创建LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //封装查询条件
        questionLambdaQueryWrapper.eq(Question::getType, question.getType())
                .eq(Question::getTitle, question.getTitle())
                .ne(Question::getId, question.getId());
        //调用QuestionMapper的带条件查询的方法
        boolean exists = baseMapper.exists(questionLambdaQueryWrapper);
        if (exists) {
            //抛出异常
            throw new ExamException(555, "在<" + question.getType() + ">类型中已经存在该题目,请重新更新为其他题目");
        }
        //调用MyBatis Plus自带的方法更新题目
        baseMapper.updateById(question);
        //获取题目的答案
        QuestionAnswer answer = question.getAnswer();
        //判断是否是选择题
        if ("CHOICE".equals(question.getType())) {
            //先删除原来所有的选项
            questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, question.getId()));
            //获取所有的选项
            List<QuestionChoice> choices = question.getChoices();
            //创建一个StringBuilder对象，用于拼接选项内容
            StringBuilder choiceContent = new StringBuilder();
            //遍历
            for (int i = 0; i < choices.size(); i++) {
                //获取每一个选项
                QuestionChoice choice = choices.get(i);
                //设置ID为null
                choice.setId(null);
                //设置创建时间和更新时间为null
                choice.setCreateTime(null);
                choice.setUpdateTime(null);
                //设置选项的排序
                choice.setSort(i);
                //2.如果是选择题保存选项
                questionChoiceMapper.insert(choice);
                //判断当前选项是否是正确答案
                if (choice.getIsCorrect()) {
                    if (choiceContent.length() > 0) {
                        choiceContent.append(",");
                    }
                    choiceContent.append((char) ('A' + i));
                }
            }

            //将正确答案设置到answer中
            answer.setAnswer(choiceContent.toString());
        }
        //更新题目答案
        questionAnswerMapper.updateById(answer);
    }

    @Transactional
    @Override
    public Boolean deleteQuestion(Long id) {
        //删除题目之前要判断一下，当前题目是否被试卷使用
        Long count = paperQuestionMapper.selectCount(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getQuestionId, id));
        if(count > 0){
            //抛出异常
            throw new ExamException(666,"当前题目已经被试卷使用，不能删除");
        }
        //根据id删除题目
        baseMapper.deleteById(id);
        //根据id删除题目答案
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId,id));
        //根据id删除题目的选项
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId,id));
        return true;
    }

    @Override
    public List<Question> getPopularQuestions(Integer size) {
        //从Redis中获取热门题目的id
        Set<Object> redisQuestionIds = redisTemplate.opsForZSet().reverseRange("question:popular", 0, size - 1);
        List<Question> redisQuestions = new ArrayList<>();
        //根据Redis中的id值从数据库中将题目查询出来
        redisQuestions = baseMapper.selectList(new LambdaQueryWrapper<Question>().in(Question::getId, redisQuestionIds));
        //将集合中的题目顺序反转
        Collections.reverse(redisQuestions);
        //创建一个包含所有前端需要的热门题目的集合
//        List<Question> returnList = new ArrayList<>();
        //获取Redis中热门题目的数量
        int redisSize = redisQuestionIds.size();
        //计算差值
        int diff = size - redisSize;
        if(diff > 0){
            //证明Redis中的热门题目数量不够，将数据库中最新的查询出来补全热门题目
            //创建LambdaQueryWrapper对象，用于构建查询条件
            LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
            //封装查询条件
            questionLambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            //查询题目需要是Redis中不包含的
            questionLambdaQueryWrapper.notIn(Question::getId,redisQuestionIds);
            //在整个sql语句的最后添加limit
            questionLambdaQueryWrapper.last("limit "+diff);
            //执行带条件的查询
            List<Question> diffQuestions = baseMapper.selectList(questionLambdaQueryWrapper);
            //将查询出来的题目添加到redisQuestions中
            redisQuestions.addAll(diffQuestions);
        }
        System.out.println("redisQuestions = " + redisQuestions);
        return redisQuestions;
    }

    @Transactional
    @Override
    public int batchImportQuetions(List<QuestionImportVo> questions) {
        //验空
        if(CollectionUtils.isEmpty(questions)){
            throw new ExamException(999,"导入的题目不能为空");
        }
        //设置一个变量统计导入成功的题目数量
        int successCount = 0;
        //遍历questions
        for (QuestionImportVo questionImportVo : questions) {
            //创建Question对象
            Question question = new Question();
            //使用BeanUtils工具类复制相同的属性
            BeanUtils.copyProperties(questionImportVo,question);
            //判断是否是选择题
            if("CHOICE".equals(questionImportVo.getType())){
                //获取所有的选项
                List<QuestionImportVo.ChoiceImportDto> choices = questionImportVo.getChoices();
                //创建一个List<QuestionChoice>集合，用于保存选项
                List<QuestionChoice> questionChoiceList = new ArrayList<>();
                //遍历choices
                for (QuestionImportVo.ChoiceImportDto choice : choices) {
                    //创建QuestionChoice对象
                    QuestionChoice questionChoice = new QuestionChoice();
                    //使用BeanUtils工具类复制相同的属性
                    BeanUtils.copyProperties(choice,questionChoice);
                    //将QuestionChoice对象添加到List<QuestionChoice>集合中
                    questionChoiceList.add(questionChoice);
                }
                //将List<QuestionChoice>集合设置到Question对象中
                question.setChoices(questionChoiceList);
            }
            //创建QuestionAnswer对象、
            QuestionAnswer questionAnswer = new QuestionAnswer();
            //设置答案
            //判断是否是判断题，如果是判断题，将true或false转换为大写
//            if("JUDGE".equals(questionImportVo.getType())){
//                questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
//            }else{
//                //是简答题直接设置
//                questionAnswer.setAnswer(questionImportVo.getAnswer());
//            }
            questionAnswer.setAnswer("JUDGE".equals(questionImportVo.getType())?questionImportVo.getAnswer().toUpperCase():questionImportVo.getAnswer());
            //将QuestionAnswer对象设置到Question对象中
            question.setAnswer(questionAnswer);
            try {
                 //将题目插入到数据库中
                createQuestion(question);
                //如果插入成功，successCount++
                successCount++;
            } catch (Exception e) {
                throw new ExamException(1000,"第"+successCount+1+"题<"+question.getTitle()+">插入失败，失败原因："+e.getMessage());
            }
        }
        return successCount;
    }

    //给题目设置答案和选项的方法
    private void fillAnswerAndChoices(List<Question> pageQuestionList) {
        //获取所有题目的ID
        List<Long> questionIds = pageQuestionList.stream().map(Question::getId).collect(Collectors.toList());
        //根据题目ID查询所有的答案
        LambdaQueryWrapper<QuestionAnswer> questionAnswerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //封装查询条件
        questionAnswerLambdaQueryWrapper.in(QuestionAnswer::getQuestionId, questionIds);
        //调用QuestionAnswerMapper的带条件查询的方法
        List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(questionAnswerLambdaQueryWrapper);
        //将答案以题目ID为key，答案列表为value，转换为Map
        Map<Long, QuestionAnswer> questionAnswerMap = questionAnswers.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, q -> q));
        //遍历所有的题目
        pageQuestionList.forEach(question -> {
            //根据题目ID获取当前题目的答案
            QuestionAnswer questionAnswer = questionAnswerMap.get(question.getId());
            //给题目设置答案
            question.setAnswer(questionAnswer);
            //判断当前题是不是选择题或判断题
            if("CHOICE".equals(question.getType()) || "JUDGE".equals(question.getType())){
                //根据题目id获取所有的选项
                //创建LambdaQueryWrapper对象，用于构建查询条件
                LambdaQueryWrapper<QuestionChoice> questionChoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
                //封装查询条件
                questionChoiceLambdaQueryWrapper.eq(QuestionChoice::getQuestionId,question.getId());
                //调用QuestionChoiceMapper的带条件查询的方法
                List<QuestionChoice> questionChoices = questionChoiceMapper.selectList(questionChoiceLambdaQueryWrapper);
                //排序
                questionChoices.sort((o1, o2) -> o1.getSort().compareTo(o2.getSort()));
                //将选项设置到题目中
                question.setChoices(questionChoices);
            }
        });
    }
}